package repo

import (
	"context"
	"fmt"
	"github.com/goccy/go-json"
	"go-caipu/pkg/plugins/log"
	"go-caipu/pkg/setting"
	"net/url"
	"strings"
)

type Manager struct {
	client  *Client
	baseURL string

	log log.PrettyLogger
}

func ProvideService(cfg *setting.Cfg) (*Manager, error) {
	defaultBaseURL, err := url.JoinPath(cfg.PluginCfg.CaiPuComURL, "")
	if err != nil {
		return nil, err
	}
	return New(false, defaultBaseURL, log.NewPrettyLogger("plugin.repository")), nil
}
func New(skipTLSVerify bool, baseURL string, logger log.PrettyLogger) *Manager {
	return &Manager{
		client:  newClient(skipTLSVerify, logger),
		baseURL: baseURL,
		log:     logger,
	}
}

// GetPluginArchive fetches the requested plugin archive
func (m *Manager) GetPluginArchive(ctx context.Context, pluginID, version string, opts CompatOpts) (*PluginArchive, error) {
	dlOpts, err := m.GetPluginDownloadOptions(ctx, pluginID, version, opts)
	if err != nil {
		return nil, err
	}

	return m.client.download(ctx, dlOpts.PluginZipURL, dlOpts.Checksum, opts)
}

// GetPluginArchiveByURL fetches the requested plugin archive from the provided `pluginZipURL`
func (m *Manager) GetPluginArchiveByURL(ctx context.Context, pluginZipURL string, compatOpts CompatOpts) (*PluginArchive, error) {
	return m.client.download(ctx, pluginZipURL, "", compatOpts)
}

// GetPluginDownloadOptions 获取安装下载插件所需的选项
// 参数:
//   - ctx: 上下文
//   - pluginID: 插件ID
//   - version: 插件版本
//   - opts: 插件安装选项
//
// 返回值:
//   - error: 安装过程中可能出现的错误
func (m *Manager) GetPluginDownloadOptions(ctx context.Context, pluginID, version string, opts CompatOpts) (*PluginDownloadOptions, error) {
	plugin, err := m.pluginMetadata(pluginID, opts)
	if err != nil {
		return nil, err
	}
	v, err := m.selectVersion(&plugin, version, opts)
	if err != nil {
		return nil, err
	}
	// Plugins which are downloaded just as sourcecode zipball from GitHub do not have checksum
	var checksum string
	if v.Arch != nil {
		archMeta, exists := v.Arch[opts.OSAndArch()]
		if !exists {
			archMeta = v.Arch["any"]
		}
		checksum = archMeta.SHA256
	}
	return &PluginDownloadOptions{
		Version:      v.Version,
		Checksum:     checksum,
		PluginZipURL: fmt.Sprintf("%s/api/plugin/%s/versions/%s/download", m.baseURL, pluginID, v.Version),
	}, nil
}

// pluginMetadata 获取当前版本和操作系统，可使用的插件版本列表
func (m *Manager) pluginMetadata(pluginID string, opts CompatOpts) (Plugin, error) {
	m.log.Debugf("Fetching metadata for plugin \"%s\" from repo %s", pluginID, m.baseURL)

	u, err := url.Parse(m.baseURL)
	if err != nil {
		return Plugin{}, err
	}
	u.Path = fmt.Sprintf("/api/plugin/%s/versions", pluginID)

	body, err := m.client.sendReq(u, opts)
	if err != nil {
		return Plugin{}, err
	}

	var data Plugin

	err = json.Unmarshal(body, &data)
	if err != nil {
		m.log.Error("Failed to unmarshal plugin repo response error", err)
		return Plugin{}, err
	}
	return data, nil
}

func (m *Manager) selectVersion(plugin *Plugin, version string, compatOpts CompatOpts) (*Version, error) {
	version = normalizeVersion(version)

	var ver Version
	latestForArch := latestSupportedVersion(plugin, compatOpts)
	if latestForArch == nil {
		return nil, ErrArcNotFound{
			PluginID:   plugin.ID,
			SystemInfo: compatOpts.OSAndArch(),
		}
	}
	if version == "" {
		return latestForArch, nil
	}
	for _, v := range plugin.Versions {
		if v.Version == version {
			ver = v
			break
		}
	}

	if len(ver.Version) == 0 {
		m.log.Debugf("Requested plugin version %s v%s not found but potential fallback version '%s' was found",
			plugin.ID, version, latestForArch.Version)
		return nil, ErrVersionNotFound{
			PluginID:         plugin.ID,
			RequestedVersion: version,
			SystemInfo:       compatOpts.String(),
		}
	}
	if !supportsCurrentArch(&ver, compatOpts) {
		m.log.Debugf("Requested plugin version %s v%s is not supported on your system but potential fallback version '%s' was found",
			plugin.ID, version, latestForArch.Version)
		return nil, ErrVersionUnsupported{
			PluginID:         plugin.ID,
			RequestedVersion: version,
			SystemInfo:       compatOpts.String(),
		}
	}
	return &ver, nil
}

// 检查版本是否支持当前架构
func supportsCurrentArch(version *Version, opts CompatOpts) bool {
	if version.Arch == nil {
		return true
	}
	for arch := range version.Arch {
		if arch == opts.OSAndArch() || arch == "any" {
			return true
		}
	}
	return false
}

// 查找最新的支持的版本
func latestSupportedVersion(plugin *Plugin, opts CompatOpts) *Version {
	for _, version := range plugin.Versions {
		if supportsCurrentArch(&version, opts) {
			return &version
		}
	}
	return nil
}

// normalizeVersion 移除版本名称前导字符
func normalizeVersion(version string) string {
	normalized := strings.ReplaceAll(version, " ", "")
	if strings.HasPrefix(normalized, "^") || strings.HasPrefix(normalized, "v") {
		return normalized[1:]
	}
	return normalized
}
